package impl

// 此文件，实现博客管理模块的接口

import (
	"context"
	"fmt"
	"time"

	"dario.cat/mergo"
	"gitee.com/liuxiaowei123/vblog-01/vblog-01/vblog/blog"
)

func (b *blogServiceImpl) CreateBlog(c context.Context, req *blog.CreateBlogRequest) (*blog.Blog, error) {
	ins := blog.NewBlog(req)
	// 此段代码前提，要有存入数据库对象实例
	err := b.db.WithContext(c).Create(ins).Error
	if err != nil {
		return nil, err
	}
	return ins, nil
}

func (b *blogServiceImpl) UpdateBlogStatus(c context.Context, req *blog.UpdateBlogStatusRequest) (*blog.Blog, error) {
	return nil, nil
}

func (b *blogServiceImpl) UpdateBlog(c context.Context, req *blog.UpdateBlogRequest) (*blog.Blog, error) {
	// 1.查询用户
	ins, err := b.DescribeBlog(c, blog.NewDescribeBlogRequest(req.BlogId))
	if err != nil {
		return nil, err
	}
	// 2.判断更新类型，全量、增量
	switch req.UpdateMode {
	// 全量更新
	case blog.UPDATE_MODE_PUT:
		ins.CreateBlogRequest = req.CreateBlogRequest
		// 增量更新
	case blog.UPDATE_MODE_PATCH:
		/* mergo.WithOverride 表示在合并过程中，如果遇到相同的字段，源结构体(req.CreateBlogRequest）的值
		会覆盖目标结构体(ins.CreateBlogRequest）的值。*/
		err := mergo.Merge(ins.CreateBlogRequest, req.CreateBlogRequest, mergo.WithOverride)
		if err != nil {
			return nil, err
		}
	default:
		return nil, fmt.Errorf("unknown update mode: %d", req.UpdateMode)
	}
	// 3.更新
	ins.UpdatedAt = time.Now().Unix()
	// err = b.db.WithContext(c).Where("id = ?", req.BlogId).Updates(ins).Error
	// 不写Where("id = ?", req.BlogId)也可以的，因为先查询后，在更新的
	/* 关于Update(ins)和Updates(ins)，如果你只想更新一个特定的记录或数据，使用 Update(ins) 可能更合适;
	如果你想根据某些条件更新多个记录或数据，那么使用 Updates(ins) 可能更合适*/
	err = b.db.WithContext(c).Updates(ins).Error
	if err != nil {
		return nil, err
	}
	return ins, nil
}

func (b *blogServiceImpl) DeleteBlog(c context.Context, req *blog.DeleteBlogRequest) error {
	// 1.先查询log
	ins, err := b.DescribeBlog(c, blog.NewDescribeBlogRequest(req.BlogId))
	if err != nil {
		return err
	}
	// 2.删除
	err = b.db.WithContext(c).Delete(ins).Error
	if err != nil {
		return err
	}
	return nil
}

func (b *blogServiceImpl) QueryBlog(c context.Context, req *blog.QueryBlogRequest) (*blog.BlogSet, error) {
	// 组装查询条件
	// 关于Model的用法？ 这里的Model应该可以换成Find嘛?
	/*不可以；因为Model(&blog.Blog{})是用于设置查询的模型，这个模型是你希望在数据库中查询的数据类型
	而Find(&blog.Blog{})则表示执行查询并将结果存储在提供的指针中，前提要有查询条件才行*/
	query := b.db.WithContext(c).Model(&blog.Blog{})
	ins := blog.NewBlogSet()
	// 组合条件查询
	// 根据blog状态查询
	if req.Status != nil {
		// 默认Status是nil
		query = query.Where("status = ?", *req.Status)

	}
	// 根据文章标题的关键字查询
	if req.Keywords != "" {
		// 关键字过滤: 模糊匹配,  Golang入门   Golang
		// ? 占位符 '%Test12%'
		query = query.Where("title LIKE ?", "%"+req.Keywords+"%")
	}
	// 根据blog用户查询
	if len(req.Usernames) > 0 {
		// gorm 会把切片[]string 自动转换为 --> (xxx,xxx)
		query = query.Where("create_by IN ?", req.Usernames)
	}

	// 1.查询blog总数量
	// 要在Count中定义一个BlogSet实例,来调用Total属性
	// SELECT count(*) FROM `blogs`
	err := query.Count(&ins.Total).Error
	if err != nil {
		return nil, err
	}

	// 2.查询当前页数
	// Find(&ins.Items),Find(ins.Items)是错误写法，因为要查询并存储多个blog，所以要用切片属性
	// &ins.Items是指针类型，ins.Items是切片类型，而这里要用指针类型
	// SELECT * FROM `blogs` LIMIT 10
	err = query.Order("created_at DESC").Offset(req.Offset()).Limit(req.PageSize).Find(&ins.Items).Error
	if err != nil {
		return nil, err
	}
	// 如果用切片类型，报错：panic: reflect: reflect.Value.Set using unaddressable value [recovered]
	// panic: reflect: reflect.Value.Set using unaddressable value

	return ins, nil
}

// DescribeBlog和QueryBlog的区别在于：传入的参数不同，DescribeBlog通过传入id来查询详细的blog，
// 而QueryBlog返回的则是某一页其中包括多个blog
func (b *blogServiceImpl) DescribeBlog(c context.Context, req *blog.DescribeBlogRequest) (*blog.Blog, error) {
	// 为什么Model(&blog.Blog{})不能写成Model(ins)？
	/* 因为ins的类型是*blog.Blog，而b.db.WithContext(c)期望的模型类型是*blog.Blog{}。这两个类型是不同的，
	即使它们指向相同的数据结构，也不能直接替换。*/
	query := b.db.WithContext(c).Model(&blog.Blog{})
	ins := blog.NewBlog(blog.NewCreateBlogRequest())
	err := query.Where("id = ?", req.BlogId).First(ins).Error
	if err != nil {
		return nil, err
	}
	return ins, nil
}

func (b *blogServiceImpl) AuditBlog(c context.Context, req *blog.AuditBlogRequest) (*blog.Blog, error) {
	// 1.查询blog
	d := blog.NewDescribeBlogRequest(req.BlogId)
	ins, err := b.DescribeBlog(c, d)
	if err != nil {
		return nil, err
	}

	// 2.更新blog中的关于审核字段
	// 所以要提前增加blog中的字段
	ins.IsAuditPass = req.IsAuditPass
	ins.AuditAt = time.Now().Unix()
	// 是Updates(ins)，而不是Update(ins)
	// err = b.db.WithContext(c).Updates(ins).Error
	err = b.db.WithContext(c).Where("id = ?", req.BlogId).Updates(ins).Error
	if err != nil {
		return nil, err
	}

	return ins, nil
}
